import os
import random
import time
import threading
import pygame
from datetime import datetime, time as dt_time, timedelta, date
import schedule
import chinese_calendar as calendar
import logging
from logging.handlers import TimedRotatingFileHandler

from ctypes import cast, POINTER
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume

# 配置
MUSIC_FOLDER = "D:/auto-play-music/MP3"  # MP3文件夹路径
PLAY_DURATION = 5 * 60  # 总播放时长（5分钟）
level = 0.9  # 音量调到90
LOG_FILE = "D:/auto-play-music/music_player.log"  # 日志文件路径
FIXED_PLAYLIST_FILE = "D:/auto-play-music/config/fixedMusicConfig.txt"  # 固定播放列表文件

# 定义播放时间段 (开始时间)
PLAY_SCHEDULES = [
    dt_time(10, 0),  # 10:00
    dt_time(15, 0),  # 15:00
]


# 设置日志
def setup_logging():
    # 创建日志目录（如果不存在）
    log_dir = os.path.dirname(LOG_FILE)
    if log_dir and not os.path.exists(log_dir):
        os.makedirs(log_dir)

    # 配置日志
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)

    # 创建按天滚动的日志处理器
    file_handler = TimedRotatingFileHandler(
        LOG_FILE, when="midnight", interval=1, backupCount=7, encoding="utf-8"
    )
    file_handler.setFormatter(logging.Formatter(
        "%(asctime)s - %(levelname)s - %(message)s"
    ))

    # 同时输出到控制台
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(logging.Formatter(
        "%(asctime)s - %(levelname)s - %(message)s"
    ))

    logger.addHandler(file_handler)
    logger.addHandler(console_handler)

    return logger


logger = setup_logging()

# 获取默认音频设备
try:
    devices = AudioUtilities.GetSpeakers()
    interface = devices.Activate(
        IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
    volume = cast(interface, POINTER(IAudioEndpointVolume))
except Exception as e:
    logger.error(f"获取默认音频设备异常: {str(e)}")
    pass


def unmute_system():
    """取消静音"""
    try:
        # 显式传入布尔值 False 代表取消静音，传入 None 作为 pguidEventContext 参数
        volume.SetMute(False, None)
        logger.info("已取消静音")
    except Exception as e:
        logger.error(f"取消静音时出错: {e}")


def set_system_volume():
    """
    设置系统音量 (0.0 到 1.0)
    level: float, 0.0 是静音，1.0 是最大音量
    """
    try:
        volume.SetMasterVolumeLevelScalar(level, None)
        logger.info(f"系统音量已设为 {level * 100:.0f}%")
    except Exception as e:
        logger.error(f"设置音量时出错: {e}")


def mute_system():
    """开启静音"""
    try:
        # 显式传入布尔值 True 代表开启静音，传入 None 作为 pguidEventContext 参数
        volume.SetMute(True, None)
        logger.info("已开启静音")
    except Exception as e:
        logger.error(f"开启静音时出错: {e}")


def is_today_workday():
    today = datetime.now().date()
    # today = date(2025, 10, 1)
    return calendar.is_workday(today)


def get_fixed_playlist():
    """读取固定播放列表"""
    fixed_songs = []
    if os.path.exists(FIXED_PLAYLIST_FILE):
        try:
            with open(FIXED_PLAYLIST_FILE, 'r', encoding='utf-8') as f:
                for line in f:
                    filename = line.strip()
                    if filename:
                        # 在音乐文件夹中查找对应的文件
                        full_path = os.path.join(MUSIC_FOLDER, filename)
                        if os.path.exists(full_path):
                            fixed_songs.append(full_path)
                        else:
                            logger.warning(f"固定播放列表中的文件不存在: {filename}")
            logger.info(f"读取到 {len(fixed_songs)} 首固定播放歌曲")
        except Exception as e:
            logger.error(f"读取固定播放列表时出错: {e}")
    else:
        logger.info("固定播放列表文件不存在，将只播放随机音乐")

    return fixed_songs


def get_all_music_files():
    """获取D盘MP3文件夹下所有音乐文件"""
    if not os.path.exists(MUSIC_FOLDER):
        logger.error(f"错误：文件夹不存在 - {MUSIC_FOLDER}")
        return []

    music_files = []
    for root, _, files in os.walk(MUSIC_FOLDER):
        for file in files:
            if file.lower().endswith('.mp3'):
                music_files.append(os.path.join(root, file))

    if not music_files:
        logger.warning("警告：未找到MP3文件！")
        return []

    return music_files


def play_random_music(scheduled_end_time=None):
    # 判断当前日期是不是工作日
    today_str = datetime.now().strftime('%Y-%m-%d')
    if not is_today_workday():
        logger.info(f"今天是 {today_str}，属于节假日或周末，程序将保持运行但不播放音乐")
        return
    logger.info(f"今天是 {today_str}，属于工作日，程序将播放音乐")

    # 检查当前时间是否在允许播放的时间段内
    if not is_in_play_window():
        logger.info("当前不在预定的播放时间段内")
        return

    """
    先播放固定列表音乐，然后随机播放其他音乐
    :param scheduled_end_time: 预定的结束时间（datetime对象）
    """
    # 获取固定播放列表
    fixed_songs = get_fixed_playlist()

    # 获取所有音乐文件
    all_music_files = get_all_music_files()
    if not all_music_files:
        return

    # 从所有音乐文件中排除固定播放的歌曲，得到随机播放的歌曲列表
    random_songs = [song for song in all_music_files if song not in fixed_songs]

    # 初始化音频系统
    pygame.mixer.init()

    # 记录已播放的随机歌曲索引
    played_random_indices = set()

    def play_next_song(current_song_list, is_fixed_playlist=True):
        """播放下一首歌曲"""
        if not current_song_list:
            return None

        if is_fixed_playlist:
            # 固定列表：按顺序播放
            song_path = current_song_list.pop(0)
            playlist_type = "固定列表"
        else:
            # 随机列表：随机选择未播放的歌曲
            if len(played_random_indices) >= len(current_song_list):
                logger.info("所有随机歌曲已播放一遍，重新开始随机选择")
                played_random_indices.clear()

            available_indices = [i for i in range(len(current_song_list)) if i not in played_random_indices]
            if not available_indices:
                return None

            current_index = random.choice(available_indices)
            played_random_indices.add(current_index)
            song_path = current_song_list[current_index]
            playlist_type = "随机列表"

        logger.info(f"正在播放[{playlist_type}]: {os.path.basename(song_path)}")
        # 取消静音
        unmute_system()
        # 设置音量
        set_system_volume()
        pygame.mixer.music.load(song_path)
        pygame.mixer.music.play()
        return song_path

    # 计算剩余播放时间
    if scheduled_end_time:
        remaining_time = (scheduled_end_time - datetime.now()).total_seconds()
        if remaining_time <= 0:
            logger.info("预定结束时间已过，不播放")
            return
    else:
        remaining_time = PLAY_DURATION

    # 创建固定播放列表的副本（避免修改原列表）
    fixed_playlist = fixed_songs.copy()

    # 播放第一首歌曲（优先固定列表）
    current_playlist = fixed_playlist if fixed_playlist else random_songs
    is_current_fixed = bool(fixed_playlist)

    play_next_song(current_playlist, is_current_fixed)

    # 创建停止线程
    def stop_playback():
        pygame.mixer.music.stop()
        logger.info("播放已停止")
        # 开启静音
        mute_system()

    # 在预定结束时间停止
    stop_timer = threading.Timer(remaining_time, stop_playback)
    stop_timer.daemon = True
    stop_timer.start()

    # 监听播放状态
    end_time = time.time() + remaining_time
    while time.time() < end_time:
        if not pygame.mixer.music.get_busy():
            # 当前歌曲播放完毕，准备播放下一首

            # 如果当前正在播放固定列表且还有固定歌曲
            if is_current_fixed and fixed_playlist:
                # 继续播放固定列表中的下一首
                play_next_song(fixed_playlist, True)
            else:
                # 固定列表已播放完或没有固定列表，切换到随机播放
                if is_current_fixed:  # 刚刚从固定列表切换到随机列表
                    logger.info("固定列表播放完毕，开始随机播放")
                    is_current_fixed = False

                # 播放随机歌曲
                play_next_song(random_songs, False)

        time.sleep(0.1)

    # 确保停止
    pygame.mixer.music.stop()
    # 开启静音
    mute_system()


def check_missed_schedules():
    """检查是否有错过的播放时间段（当前时间仍在5分钟窗口内）"""
    now = datetime.now()
    for schedule_time in PLAY_SCHEDULES:
        schedule_dt = datetime.combine(now.date(), schedule_time)
        scheduled_end_time = schedule_dt + timedelta(minutes=5)

        # 如果当前时间在计划时间后的5分钟内（且未超过结束时间）
        if schedule_dt <= now <= scheduled_end_time:
            logger.info(f"检测到延迟启动，当前时间在 {schedule_time.strftime('%H:%M')} 的播放窗口内")
            logger.info(f"立即开始播放，将在预定的结束时间 {scheduled_end_time.time().strftime('%H:%M:%S')} 停止")
            play_random_music(scheduled_end_time)
            break


def is_in_play_window():
    """检查当前时间是否在任意一个播放时间段内（开始时间后的5分钟内）"""
    now = datetime.now()
    logger.info(f"当前时间： {now}")
    for schedule_time in PLAY_SCHEDULES:
        schedule_dt = datetime.combine(now.date(), schedule_time)
        scheduled_end_time = schedule_dt + timedelta(minutes=5)
        if schedule_dt <= now <= scheduled_end_time:
            return True
    return False


def schedule_checker():
    """定时任务检查"""
    # 首次启动时检查是否有错过的播放
    check_missed_schedules()

    # 然后正常执行定时检查
    while True:
        schedule.run_pending()
        time.sleep(1)


if __name__ == "__main__":
    logger.info("随机音乐播放器已启动")
    logger.info(f"音乐文件夹: {MUSIC_FOLDER}")
    logger.info("播放时间段:")
    for t in PLAY_SCHEDULES:
        logger.info(
            f"  - {t.strftime('%H:%M')} 到 {(datetime.combine(datetime.today(), t) + timedelta(minutes=5)).time().strftime('%H:%M')}")

    # 初始化pygame（仅音频）
    try:
        pygame.mixer.init()
    except Exception as e:
        logger.error(f"初始化pygame（仅音频）异常: {str(e)}")
        pass

    # 定时任务
    for schedule_time in PLAY_SCHEDULES:
        schedule.every().day.at(schedule_time.strftime("%H:%M")).do(
            lambda st=schedule_time: play_random_music(
                datetime.combine(datetime.today(), st) + timedelta(minutes=5)
            )
        )

    # 启动定时任务线程
    threading.Thread(target=schedule_checker, daemon=True).start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        pygame.mixer.music.stop()
        # 开启静音
        mute_system()
        logger.info("播放器已关闭")